package avicit.bdp.oss.rest;

import avicit.bdp.oss.dto.ObjectDto;
import avicit.bdp.oss.service.object.ObjectService;
import avicit.bdp.oss.utils.errors.PermException;
import avicit.bdp.oss.utils.upload.entity.OssUploadDto;
import avicit.bdp.oss.utils.upload.entity.TaskInfoDto;
import avicit.bdp.oss.utils.upload.service.impl.OssUploadService;
import avicit.bdp.oss.utils.upload.utils.common.Constant;
import avicit.platform6.core.context.ThreadContextHelper;
import avicit.platform6.core.rest.msg.QueryRespBean;
import avicit.platform6.core.rest.msg.ResponseMsg;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.util.List;
import java.util.Map;

/**
 * @金航数码科技有限责任公司
 * @作者：developer
 * @邮箱：developer@avic-digital.com
 * @创建时间： 2020-11-12
 * @类说明：ObjectRest
 * @修改记录：
 * @注意事项：
 * @主要功能：Object相关操作Rest接口
 */
@RestController
@RequestMapping("/api/bdp/oss/object")
@Api(tags = "object操作接口")
public class ObjectRest {
    private static final Logger logger = LoggerFactory.getLogger(BucketRest.class);

    @Autowired
    private ObjectService objectService;

    @Autowired
    private OssUploadService ossUploadService;

    /**
     * 新建目录
     * @param objectDto
     * @return
     */
    @PostMapping("/makeDirectory")
    @ApiOperation(value = "新建目录")
    public ResponseMsg makeDirectory(@RequestBody ObjectDto objectDto) {
        ResponseMsg responseMsg = new ResponseMsg<>();

        this.objectService.makeDirectory(objectDto);

        return responseMsg;
    }

    /**
     * 列举对象
     * @param sort
     * @param objectName
     * @param pageNo
     * @param pageSize
     * @return
     */
    @GetMapping("/listObjects")
    @ApiOperation(value = "列举对象")
    public ResponseMsg<QueryRespBean<ObjectDto>> listObjects(
            @ApiParam(value = "排序，按创建时间倒序：1，按创建时间升序：2，按名称升序：3，按名称倒序：4", name = "sort") @RequestParam(required = false) Integer sort,
            @ApiParam(value = "当前页", name = "pageNo", required = true)  @RequestParam(defaultValue = "1") Integer pageNo,
            @ApiParam(value = "每页显示条数", name = "pageSize", required = true) @RequestParam(defaultValue = "10") Integer pageSize,
            @ApiParam(value = "桶名称", name = "bucketName") @RequestParam(required = false) String bucketName,
            @ApiParam(value = "前缀", name = "prefix") @RequestParam(required = false) String prefix,
            @ApiParam(value = "对象名称", name = "objectName") @RequestParam(required = false) String objectName) {
        ResponseMsg<QueryRespBean<ObjectDto>> responseMsg = new ResponseMsg<>();

        responseMsg.setResponseBody(this.objectService.listObjects(sort, pageNo, pageSize, bucketName, prefix, objectName));

        return responseMsg;
    }

    /**
     * 上传对象
     * @param
     * @return
     * @throws Exception
     */
    @PostMapping("/putObject")
    @ApiOperation(value = "上传对象")
    public ResponseMsg putObject(
            @ApiParam(value = "MultipartFile", name = "MultipartFile") @RequestParam(required = false) MultipartFile file,
            @ApiParam(value = "桶名称", name = "bucketName") @RequestParam(required = false) String bucketName,
            @ApiParam(value = "前缀", name = "prefix") @RequestParam(required = false) String prefix,
            @ApiParam(value = "对象名称", name = "objectName") @RequestParam(required = false) String objectName) {
        ResponseMsg responseMsg = new ResponseMsg<>();

        ObjectDto objectDto =
                ObjectDto.builder().bucketName(bucketName).prefix(prefix).objectName(objectName).file(file).build();
        this.objectService.putObject(objectDto);

        return responseMsg;
    }

    /**
     * 下载对象
     * @param id
     * @param response
     */
    @GetMapping("/getObject")
    @ApiOperation(value = "下载对象")
    public void getObject(
            @ApiParam(value = "对象ID", name = "id")@RequestParam(required = false) String id,
            HttpServletRequest request,
            HttpServletResponse response) {
        this.objectService.getObject(id, request, response);
    }

    /**
     * 获取对象信息
     * @param id
     * @return
     */
    @GetMapping("/statObject")
    @ApiOperation(value = "获取对象信息")
    public ResponseMsg<ObjectDto> statObject(
            @ApiParam(value = "对象ID", name = "id")@RequestParam(required = false) String id) {
        ResponseMsg<ObjectDto> responseMsg = new ResponseMsg<>();

        responseMsg.setResponseBody(this.objectService.statObject(id));

        return responseMsg;
    }

    /**
     * 删除对象(包括文件、目录)
     * @param objectDto
     * @return
     */
    @DeleteMapping("/removeObject")
    @ApiOperation(value = "删除对象")
    public ResponseMsg removeObject(@RequestBody ObjectDto objectDto) {
        ResponseMsg responseMsg = new ResponseMsg<>();

        this.objectService.removeObject(objectDto);

        return responseMsg;
    }

    /**
     * 分享对象
     * @param id
     * @return
     */
    @GetMapping("/shareObject")
    @ApiOperation(value = "分享对象")
    public ResponseMsg<String> shareObject(
            HttpServletRequest request,
            @ApiParam(value = "对象ID", name = "id")@RequestParam(required = true) String id,
            @ApiParam(value = "过期时间", name = "expire")@RequestParam(required = false) Integer expire) {
        ResponseMsg<String> responseMsg = new ResponseMsg<>();

        responseMsg.setResponseBody(this.objectService.shareObject(request, id, expire));

        return responseMsg;
    }

    /**
     * 下载对象-根据bucketName、objectName
     * @param bucketName
     * @param objectName
     * @param response
     */
    @GetMapping("/getObjectByName")
    @ApiOperation(value = "下载对象")
    public void getObjectByName(
            @ApiParam(value = "桶名称", name = "bucketName")@RequestParam(required = true) String bucketName,
            @ApiParam(value = "对象名称", name = "objectName")@RequestParam(required = true) String objectName,
                                HttpServletResponse response) {
        this.objectService.getObjectByName(bucketName, objectName, response);
    }

    /**
     * 获取所有对象-根据bucketName、prefix
     * @param bucketName
     * @param prefix
     * @return
     */
    @GetMapping("/getObjects")
    @ApiOperation(value = "下载对象")
    public ResponseMsg<List<String>> getObjects(
    @ApiParam(value = "桶名称", name = "bucketName") @RequestParam(required = true) String bucketName,
    @ApiParam(value = "前缀", name = "prefix") @RequestParam(required = false) String prefix) {
        ResponseMsg<List<String>> responseMsg = new ResponseMsg<>();

        responseMsg.setResponseBody(this.objectService.getObjects(bucketName, prefix));

        return responseMsg;
    }

    /**
     * 查询系统中最大的五个对象信息
     * @return
     */
    @GetMapping("/getMaxObjects")
    @ApiOperation(value = "查询系统中最大的五个对象信息")
    public ResponseMsg<List<ObjectDto>> getMaxObjects() {
        ResponseMsg<List<ObjectDto>> responseMsg = new ResponseMsg<>();

        responseMsg.setResponseBody(this.objectService.getMaxObjects());

        return responseMsg;
    }

    /**
     * 初始化上传Task
     * @param
     * @return
     * @throws Exception
     */
    @PostMapping("/saveUploadTask")
    @ApiOperation(value = "初始化上传Task")
    public ResponseMsg<TaskInfoDto> saveUploadTask(@RequestBody OssUploadDto ossUploadDto) {
        ResponseMsg<TaskInfoDto> responseMsg = new ResponseMsg<>();

        responseMsg.setResponseBody(ossUploadService.initiateUploadTask(ossUploadDto));

        return responseMsg;
    }

    /**
     * 断点续传/分片上传/秒传
     * @param
     * @return
     * @throws Exception
     */
    @PostMapping("/upload")
    @ApiOperation(value = "上传对象")
    public ResponseMsg uploadChunk(
            @ApiParam(value = "OssUploadDto", name = "OssUploadDto")OssUploadDto ossUploadDto, HttpServletResponse response) {
        ResponseMsg responseMsg = new ResponseMsg<>();

        try {
            ossUploadService.uploadChunks(ossUploadDto);
        } catch (PermException pe) {
            logger.error("上传失败，桶策略或对象权限校验失败,errCode={}", HttpStatus.UNAUTHORIZED.value());
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            String userName = this.objectService.getUserNameByIds(ThreadContextHelper.getUserId());
            responseMsg.setErrorDesc(String.format("当前用户[%s]无上传权限.", userName));
        }

        return responseMsg;
    }

    /**
     * 保存上传对象元数据信息
     * @param ossUploadDto
     * @return
     */
    @PostMapping("/mergeChunks")
    @ApiOperation(value = "合并分片")
    public ResponseMsg mergeChunks(@RequestBody OssUploadDto ossUploadDto) {
        ResponseMsg responseMsg = new ResponseMsg();

        ossUploadService.mergeChunks(ossUploadDto);

        return responseMsg;
    }

    /**
     * 获取已上传对象分片信息
     * @param taskId
     * @return
     * @throws Exception
     */
    @GetMapping("/upload")
    @ApiOperation(value = "获取已上传对象分片信息")
    public ResponseMsg<Map<String, List<Integer>>> getUploadedChunkInfos(
            @ApiParam(value = "任务ID", name = "taskId", required = true) @RequestParam String taskId,
            @ApiParam(value = "任务ID", name = "bucketName", required = true) @RequestParam String bucketName,
            @ApiParam(value = "任务ID", name = "prefix", required = false) String prefix,
            @ApiParam(value = "任务ID", name = "objectName", required = true) @RequestParam String objectName,
            @ApiParam(value = "任务ID", name = "fileMd5", required = true) @RequestParam String fileMd5) {
        ResponseMsg<Map<String, List<Integer>>> responseMsg = new ResponseMsg<>();

        responseMsg.setResponseBody(ossUploadService.getUploadedChunks(taskId, bucketName, prefix, objectName, fileMd5));

        return responseMsg;
    }

    /**
     * 获取当前用户的所有上传Task
     */
    @GetMapping("/listAllTask")
    @ApiOperation(value = "列举当前用户的Task")
    public ResponseMsg<QueryRespBean<TaskInfoDto>> getAllTask(
            @ApiParam(value = "当前页", name = "pageNo", required = true) @RequestParam Integer pageNo,
            @ApiParam(value = "每页显示条数", name = "pageSize", required = true) @RequestParam Integer pageSize,
            @ApiParam(value = "任务ID", name = "taskId", required = true) @RequestParam String taskId) {
        ResponseMsg<QueryRespBean<TaskInfoDto>> responseMsg = new ResponseMsg();

        responseMsg.setResponseBody(ossUploadService.listAllTask(pageNo, pageSize, taskId));

        return responseMsg;
    }

    /**
     * 删除指定Task，多个TaskId使用英文逗号隔开
     * @param ossUploadDto
     * @return
     */
    @DeleteMapping("/deleteTask")
    @ApiOperation(value = "删除指定Task")
    public ResponseMsg deleteTask(@RequestBody OssUploadDto ossUploadDto) {
        ResponseMsg responseMsg = new ResponseMsg<>();

        ossUploadService.deleteTaskInfo(ossUploadDto);

        return responseMsg;
    }

    /**
     * 临时测试用API，后续删除
     */
    @GetMapping("/test")
    @ApiOperation(value = "临时测试接口，模拟VUE前端发送断点续传消息")
    public ResponseMsg test() {
        ResponseMsg responseMsg = new ResponseMsg();

        // step1:获取现有task，继续上传；
        Integer chunks = 20;
        long chunkSize = 10956;
        String bucketName = "datax-test";
        String prefix = "test";
        String objectName = "test.csv";
        String fileMd5 = "0123456789";
        String fileName = "D:\\test\\test.csv";
        try {
            // step1:获取task
            OssUploadDto ossUploadDto = OssUploadDto.builder()
                    .bucketName(bucketName)
                    .prefix(prefix)
                    .objectName(objectName)
                    .fileMd5(fileMd5)
                    .objectSize(chunks * chunkSize)
                    .chunkSize(chunkSize)
                    .chunks(chunks)
                    .build();
            TaskInfoDto taskInfoDto = ossUploadService.initiateUploadTask(ossUploadDto);
            taskInfoDto.setId("xxxx-123456789");

            ossUploadDto.setTaskId(taskInfoDto.getId());

            // step2:获取已上传文件分片信息
            Map<String, List<Integer>> map = ossUploadService.getUploadedChunks(
                    taskInfoDto.getId(), bucketName, prefix, objectName, fileMd5);

            // step3:上传所有分片
            for (int index = 1; index <= chunks; index++) {
                // 已经上传完成chunk不在上传
                List<Integer> list = map.get(Constant.UPLOADED_STR);
                if (list.contains(index)) {
                    continue;
                }

                File file = new File("D:\\test\\test-" + index + ".csv");
                FileInputStream input = new FileInputStream(file);
                MultipartFile multipartFile = new MockMultipartFile("file", "test.csv",
                        "text/plain", IOUtils.toByteArray(input));

                OssUploadDto tmpUploadDto = OssUploadDto.builder()
                        .taskId(taskInfoDto.getId())
                        .bucketName(bucketName)
                        .prefix(prefix)
                        .objectName(objectName)
                        .fileMd5(fileMd5)
                        .objectSize(chunks * chunkSize)
                        .chunkSize(chunkSize)
                        .chunks(chunks)
                        .chunkNo(index)
                        .file(multipartFile)
                        .build();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        ossUploadService.uploadChunks(tmpUploadDto);
                    }
                }).start();
            }

            // step4:合并分片
            ossUploadService.mergeChunks(ossUploadDto);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return responseMsg;
    }
}

